package com.art.demos.core.http.feign.java;

import com.google.api.client.http.ByteArrayContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpContent;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import feign.Request;
import feign.Response;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * This module directs Feign's http requests to <a href=
 * "https://developers.google.com/api-client-library/java/google-http-java-client/">Google
 * HTTP Client</a>.
 *
 * <pre> GitHub github = Feign.builder().client(new
 * GoogleHttpCliest()).target(GitHub.class, "https://api.github.com");
 */
public class GoogleHttpClient implements Client {

	private final HttpRequestFactory requestFactory;

	public GoogleHttpClient() {
		this(new NetHttpTransport());
	}

	public GoogleHttpClient(final HttpTransport transport) {
		this(transport.createRequestFactory());
	}

	public GoogleHttpClient(final HttpRequestFactory requestFactory) {
		this.requestFactory = requestFactory;
	}

	@Override
	public final Response execute(final Request inputRequest, final Request.Options options) throws IOException {
		// 将feign的Request转换为Google的HttpRequest
		final HttpRequest request = convertRequest(inputRequest, options);
		// 执行请求
		final HttpResponse response = request.execute();
		// 将Google的HttpResponse转换为feign的Response
		return convertResponse(inputRequest, response);
	}

	private final HttpRequest convertRequest(final Request inputRequest, final Request.Options options)
			throws IOException {
		// 设置请求体的内容
		HttpContent content = null;
		if (inputRequest.length() > 0) {
			final Collection<String> contentTypeValues = inputRequest.headers().get("Content-Type");
			String contentType = null;
			if (contentTypeValues != null && contentTypeValues.size() > 0) {
				contentType = contentTypeValues.iterator().next();
			}
			else {
				contentType = "application/octet-stream";
			}
			content = new ByteArrayContent(contentType, inputRequest.body());
		}

		// 生成请求
		final HttpRequest request = requestFactory.buildRequest(inputRequest.httpMethod().name(),
				new GenericUrl(inputRequest.url()), content);

		// 设置请求头
		final HttpHeaders headers = new HttpHeaders();
		for (final Map.Entry<String, Collection<String>> header : inputRequest.headers().entrySet()) {
			// We already set the Content-Type header via ByteArrayContent
			// Content-Type is defined as a singleton field
			// https://www.rfc-editor.org/rfc/rfc9110.html#section-8.3-7
			if (!header.getKey().equals("Content-Type")) {
				headers.set(header.getKey(), header.getValue());
			}
		}
		// Some servers don't do well with no Accept header
		if (inputRequest.headers().get("Accept") == null) {
			// 在HTTP请求中，"Accept" 头部用于指示客户端所能接受的响应内容类型。如果客户端不明确指定 "Accept"
			// 头部，服务器可能会根据默认规则来决定响应内容的类型。
			headers.setAccept("*/*");
		}
		request.setHeaders(headers);

		// Setup request options
		request.setReadTimeout(options.readTimeoutMillis())
			// 设置连接超时时间
			.setConnectTimeout(options.connectTimeoutMillis())
			// 设置是否允许重定向
			.setFollowRedirects(options.isFollowRedirects())
			// 设置是否在执行错误时抛出异常
			.setThrowExceptionOnExecuteError(false);
		return request;
	}

	private final Response convertResponse(final Request inputRequest, final HttpResponse inputResponse)
			throws IOException {
		final HttpHeaders headers = inputResponse.getHeaders();
		Integer contentLength = null;
		if (headers.getContentLength() != null && headers.getContentLength() <= Integer.MAX_VALUE) {
			contentLength = inputResponse.getHeaders().getContentLength().intValue();
		}
		return Response.builder()
			// 设置响应体
			.body(inputResponse.getContent(), contentLength)
			// 设置HTTP协议版本
			.status(inputResponse.getStatusCode())
			// 设置HTTP状态码
			.reason(inputResponse.getStatusMessage())
			// 设置HTTP响应头
			.headers(toMap(inputResponse.getHeaders()))
			// 设置HTTP请求信息
			.request(inputRequest)
			.build();
	}

	private final Map<String, Collection<String>> toMap(final HttpHeaders headers) {
		final Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
		for (final String header : headers.keySet()) {
			map.put(header, headers.getHeaderStringValues(header));
		}
		return map;
	}

}
